package com.jackeysun.widget

import android.content.Context
import android.graphics.*
import android.graphics.pdf.PdfRenderer
import android.os.Build
import android.os.ParcelFileDescriptor
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import com.jackeysun.widget.databinding.ViewPdfBinding
import kotlinx.coroutines.*
import java.io.*


class PdfView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), CoroutineScope by MainScope() {

    private val mBinding = ViewPdfBinding.inflate(LayoutInflater.from(context), this, true)

    private val pageInfos = mutableListOf<PageInfo>()
    private var mBitmap: Bitmap? = null
    private lateinit var renderer: PdfRenderer
    private var maxWidth = 0

    @RequiresApi(Build.VERSION_CODES.M)
    override fun onFinishInflate() {
        super.onFinishInflate()
    }

    fun loadFile(file: File) {
        val mFileDescriptor =
            ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
        loadPDF(mFileDescriptor)
    }

    fun loadAssets(fileName: String) {
        val mFileDescriptor = context.assets.openFd(fileName).parcelFileDescriptor
        loadPDF(mFileDescriptor)
    }

    fun loadPDF(pd: ParcelFileDescriptor) {
        mBinding.pb.visibility = View.VISIBLE
        mBinding.imageView.post {
            launch {
                try {
                    initPDF(pd)
                    val scale = scale()
                    createBitmap(scale)
                    val baos = ByteArrayOutputStream()
                    mBitmap?.compress(Bitmap.CompressFormat.PNG, 100, baos)
                    val isBm: InputStream = ByteArrayInputStream(baos.toByteArray())
                    mBinding.imageView.setImage(isBm)
                } catch (e: Exception) {
                    e.printStackTrace()
                } finally {
                    mBinding.pb.visibility = View.GONE
                }
            }

        }
    }

    private suspend fun initPDF(pd: ParcelFileDescriptor) {
        try {
            withContext(Dispatchers.IO) {
                renderer = PdfRenderer(pd)
                val pageCount = renderer.pageCount
                for (i in 0 until pageCount) {
                    val page: PdfRenderer.Page = renderer.openPage(i)
                    pageInfos.add(PageInfo(page.width, page.height))
                    page.close()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun scale(): Float {
        for (i in pageInfos.indices) {
            if (maxWidth < pageInfos[i].width) {
                maxWidth = pageInfos[i].width
            }
        }
        return mBinding.imageView.width.toFloat() / maxWidth
    }

    private suspend fun createBitmap(
        scale: Float = 1.0f,
        bgColor: Int = Color.WHITE,
        spaceColor: Int = Color.GRAY,
        space: Float = 10f
    ) {
        withContext(Dispatchers.IO) {
            var width = 0
            var height = 0
            for (i in pageInfos.indices) {
                if (width < pageInfos[i].width * scale) {
                    width = (pageInfos[i].width * scale).toInt()
                }
                height += (pageInfos[i].height * scale).toInt() + space.toInt()
            }
            height -= space.toInt()

            mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
            val canvas = Canvas(mBitmap ?: return@withContext)
            canvas.drawColor(bgColor)
            val paint = Paint()
            paint.isAntiAlias = true
            paint.isDither = true

            var top = 0f
            val pageCount = renderer.pageCount
            for (i in 0 until pageCount) {
                val page: PdfRenderer.Page = renderer.openPage(i)
                val pageWidth = (page.width * scale).toInt()
                val pageHeight = (page.height * scale).toInt()
                val pageBitmap = Bitmap.createBitmap(pageWidth, pageHeight, Bitmap.Config.ARGB_8888)
                val pageCanvas = Canvas(pageBitmap)
                pageCanvas.drawColor(bgColor)
                paint.color = Color.WHITE
                pageCanvas.drawBitmap(pageBitmap, 0f, 0f, paint)
                val r = Rect(0, 0, pageWidth, pageHeight)
                val transform = Matrix()
                transform.setScale(scale, scale)
                page.render(pageBitmap, r, transform, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)

                canvas.drawBitmap(pageBitmap, 0f, top, paint)
                if (i < pageCount - 1) {
                    top += pageBitmap.height
                    paint.color = spaceColor
                    paint.strokeWidth = space
                    canvas.drawLine(0f, top, width.toFloat(), top, paint)
                    top += space
                }
                pageBitmap.recycle()
                page.close()
            }
        }

    }

    fun release() {
        cancel()
        try {
            mBitmap?.recycle()
            renderer.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

data class PageInfo(val width: Int, val height: Int)